﻿using Tszy.Unlimited.Base.Exceptions;
using Tszy.Unlimited.Base.Infrastructure.Mappers;

namespace Tszy.Unlimited.Base.Extensions;

/// <summary>
/// 对象属性映射。
/// </summary>
public static class MapperExtensions
{
    /// <summary>
    /// 对象属性值映射。
    /// </summary>
    /// <remarks>
    /// 生成一个 T 类型的对象，并将该对象的属性值设置为 destination 对象中对应属性的值。
    /// </remarks>
    /// <typeparam name="TDestination">新数据对象类型。</typeparam>
    /// <param name="destination">目标数据对象。</param>
    /// <returns></returns>
    public static TDestination Map<TDestination>(this object destination) where TDestination : class, new()
    {
        return OnMap<TDestination>(destination);
    }

    /// <summary>
    /// 对象属性值映射。
    /// </summary>
    /// <remarks>
    /// 将 destination 对象中的属性的值赋值给 source 对象中对应属性的值。
    /// </remarks>
    /// <typeparam name="TDestination">新数据对象类型。</typeparam>
    /// <param name="source">原始数据对象。</param>
    /// <param name="destination">目标数据对象。</param>
    /// <returns></returns>
    public static TDestination Map<TDestination>(this object source, object destination) where TDestination : class, new()
    {
        return OnMap<TDestination>(source, destination);
    }

    /// <summary>
    /// 将字典
    /// </summary>
    /// <typeparam name="TDestination"></typeparam>
    /// <param name="source"></param>
    /// <returns></returns>
    public static TDestination Map<TDestination>(this IDictionary<string, object> source) where TDestination : class, new()
    {
        return TszyMapper.Instance.GetDictionaryMapper().Map<TDestination>(source);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="sources"></param>
    /// <returns></returns>
    /// <exception cref="MainApplicationException"></exception>
    private static T OnMap<T>(params object[] sources) where T : class
    {
        if (!sources.Any())
        {
            throw new MapperException("请传入需要映射的实例");
        }

        var initialSource = sources[0];
        var mappingResult = OnMap<T>(initialSource);

        // Now map the remaining source objects
        if (sources.Count() > 1)
        {
            OnMap(mappingResult, sources.Skip(1).ToArray());
        }
        return mappingResult;
    }

    private static void OnMap(object destination, params object[] sources)
    {
        if (!sources.Any())
        {
            return;
        }
        var destinationType = destination.GetType();
        foreach (var source in sources)
        {
            var sourceType = source.GetType();
            TszyMapper.Instance.GetObjectMapper(sourceType, destinationType)
                .Map(source, destination, sourceType, destinationType);
        }
    }

    private static T OnMap<T>(object source) where T : class
    {
        var destinationType = typeof(T);
        var sourceType = source.GetType();
        var mappingResult = TszyMapper.Instance.GetObjectMapper(sourceType, destinationType).Map(source, sourceType, destinationType);

        if (mappingResult == null)
            throw new MapperException("映射失败");

        return (T)mappingResult;
    }
}